package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateTimeZoneConverter;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import okhttp3.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 设备相关Service业务层处理
 *
 * @author ruoyi
 * @date 2025-08-01
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IoTDAClient client;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询设备相关
     *
     * @param id 设备相关主键
     * @return 设备相关
     */
    @Override
    public Device selectDeviceById(Long id) {
        return getById(id);
    }

    /**
     * 查询设备相关列表
     *
     * @param device 设备相关
     * @return 设备相关
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备相关
     *
     * @param device 设备相关
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return save(device) ? 1 : 0;
    }

//    /**
//     * 修改设备相关
//     *
//     * @param device 设备相关
//     * @return 结果
//     */
//    @Override
//    public int updateDevice(Device device) {
//        return updateById(device) ? 1 : 0;
//    }

    /**
     * 批量删除设备相关
     *
     * @param ids 需要删除的设备相关主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除设备相关信息
     *
     * @param id 设备相关主键
     * @return 结果
     */
//    @Override
//    public int deleteDeviceById(Long id) {
//        return removeById(id) ? 1 : 0;
//    }

    /**
     * 同步产品列表
     */
    @Override
    public void syncProductList() {
        // 获取请求参数
        ListProductsRequest request = new ListProductsRequest();
        // 设置条数
        request.setLimit(50);
        // 发起请求
        ListProductsResponse response = client.listProducts(request);
        // 判断是否成功
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("同步产品列表失败");
        }
        List<ProductSummary> products = response.getProducts();
        // 存储到redis
        redisTemplate.opsForValue().set(Constants.IOT_ALL_PRODUCT_LIST, JSONUtil.toJsonStr(response.getProducts()));
    }

    @Override
    public List<ProductVo> allProduct() {
        // 从redis中查询数据
        String productsJsonStr = (String) redisTemplate.opsForValue().get(Constants.IOT_ALL_PRODUCT_LIST);
        // 如果数据为空,返回一个空集合
        if (productsJsonStr == null) {
//            return new ArrayList<>();
            return Collections.emptyList();
        }
        // 解析数据,并返回
//        JSONObject jsonObject = JSONObject.parseObject(productsJsonStr);
//        jsonObject.get...
        // 使用hutool工具的toList方法
        List<ProductVo> result = JSONUtil.toList(productsJsonStr, ProductVo.class);
        return result;
    }

    @Override
    public void register(DeviceDto deviceDto) {
        // 判断设备名称是否存在
        Device device = this.getOne(Wrappers.<Device>lambdaQuery().eq(Device::getDeviceName, deviceDto.getDeviceName()));
        if (device != null) {
            throw new BaseException("设备名称已存在!");
        }
        // 判断设备标识是否存在
        device = this.getOne(Wrappers.<Device>lambdaQuery().eq(Device::getNodeId, deviceDto.getNodeId()));
        if (device != null) {
            throw new BaseException("设备标识已存在!");
        }
        // 判断同一位置是否绑定了相同的产品
        device = this.getOne(Wrappers.<Device>lambdaQuery()
                .eq(Device::getProductKey, deviceDto.getProductKey()) // 产品key
                .eq(Device::getBindingLocation, deviceDto.getBindingLocation()) // 绑定位置
                .eq(Device::getLocationType, deviceDto.getLocationType()) // 位置类型(随身/固定)
                .eq(deviceDto.getPhysicalLocationType() != null, // 物理位置类型
                        Device::getLocationType, deviceDto.getPhysicalLocationType())
        );
        // 设备注册 -> iot平台(华为云)  具体需要的请求参数结合官网和实际需求
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();
        body.setProductId(deviceDto.getProductKey());
        body.setDeviceName(deviceDto.getDeviceName());
        body.setNodeId(deviceDto.getNodeId());
        // 秘钥设置,因为数据库表中需要该字段
        String secret = UUID.randomUUID().toString().replaceAll("-", "");
        AuthInfo authInfo = new AuthInfo();
        authInfo.setSecret(secret);
        body.setAuthInfo(authInfo);
        request.setBody(body);

        AddDeviceResponse response = client.addDevice(request);

        // 本地保存设备
        // 属性拷贝
        Device result = BeanUtil.toBean(deviceDto, Device.class);
        // 秘钥
        result.setSecret(secret);
        // 设备id,设备绑定状态
        result.setIotId(response.getDeviceId());
        this.save(result);
    }

    @Override
    public DeviceDetailVo getDeviceDetail(String iotId) {
        // 查询本地数据库数据
        Device device = this.getOne(Wrappers.<Device>lambdaQuery().eq(Device::getIotId, iotId));
        if (device == null) {
            throw new BaseException("数据库查询失败,不存在该设备!");
        }
        // 调用华为云接口查询数据
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.setDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        if (response == null) {
            throw new BaseException("iot查询失败,不存在该设备!");
        }
        String activeTimeStr = response.getActiveTime();
        String status = response.getStatus();
        // 属性拷贝
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        deviceDetailVo.setDeviceStatus(status);
        // 日期转换
        if (activeTimeStr != null) {
            // 日期类型转换
            LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
            // 日期时区转换
            LocalDateTime activeTime_Shanghai = DateTimeZoneConverter.utcToShanghai(activeTime);
            deviceDetailVo.setActiveTime(activeTime_Shanghai);
        }
        return deviceDetailVo;
    }

    @Override
    public List<Map<String, Object>> queryServiceProperties(String iotId) {
        // 查iot
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.setDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("iot查询设备影子失败");
        }
        List<DeviceShadowData> shadow = response.getShadow();
        if (CollectionUtil.isEmpty(shadow)) {
            return Collections.emptyList();
        }
        // 获取上报数据的reported (参考返回的json数据)
        DeviceShadowProperties reported = shadow.get(0).getReported();
        // 拿到事件上报时间 进行类型&格式转换
        String eventTimeStr = reported.getEventTime();
        // 时间类型转换
        LocalDateTime eventTimeLdt = LocalDateTimeUtil.parse(eventTimeStr, "yyyyMMdd'T'HHmmss'Z'");
        // 时区格式转换
        LocalDateTime eventTime = DateTimeZoneConverter.utcToShanghai(eventTimeLdt);
        // 把数据properties转换为jsonobject,方便处理
        JSONObject jsonObject = JSONUtil.parseObj(reported.getProperties());

        // 遍历jsonobject数据,封装到list中
        List<Map<String, Object>> result = new ArrayList<>();
        jsonObject.forEach((k, v) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("functionId", k);
            map.put("value", v);
            map.put("eventTime", eventTime);
            result.add(map);
        });

        return result;
    }

    @Override
    public void updateDevice(DeviceDto dto) {
        // 判断是否只修改了设备名称,只修改设备名称不需要走判断
        Device oldDevice = this.getOne(Wrappers.<Device>lambdaQuery().eq(Device::getIotId, dto.getIotId()));
        if (ObjectUtil.notEqual(oldDevice.getBindingLocation(), dto.getBindingLocation().toString())
                && ObjectUtil.notEqual(oldDevice.getLocationType(), dto.getLocationType())) {
            // 绑定位置和位置类型如果都不相同,说明不止修改了设备名称
            // 需要判断要修改的位置是否已经绑定了同一个产品
            long count = this.count(Wrappers.<Device>lambdaQuery()
                    .eq(Device::getProductKey, dto.getProductKey()) // 产品key
                    .eq(Device::getBindingLocation, dto.getBindingLocation()) // 绑定位置
                    .eq(Device::getLocationType, dto.getLocationType()) // 位置类型(随身/固定)
                    .eq(dto.getPhysicalLocationType() != null,
                            Device::getLocationType, dto.getPhysicalLocationType()) // 物理位置类型(0-2,楼/房/床)
            );
            if (count > 0) {
                throw new BaseException("同一位置已绑定该产品");
            }
        }

        // 再修改数据库
        Device device = BeanUtil.toBean(dto, Device.class);
        device.setBindingLocation(dto.getBindingLocation().toString());
        boolean flag = this.update(device, Wrappers.<Device>lambdaQuery().eq(Device::getIotId, dto.getIotId()));
        if (!flag) {
            throw new BaseException("数据库修改设备失败");
        }

        // 最后修改Iot  只需要改一个DeviceName
        UpdateDeviceRequest request = new UpdateDeviceRequest();
        request.setDeviceId(dto.getIotId());
        UpdateDevice body = new UpdateDevice();
        body.setDeviceName(dto.getDeviceName());
        request.setBody(body);
        UpdateDeviceResponse response = client.updateDevice(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("iot修改设备失败");
        }
    }

    @Override
    public void deleteDeviceById(String iotId) {
        // 先删数据库
        boolean flag = this.remove(Wrappers.<Device>lambdaQuery().eq(Device::getIotId, iotId));
        if (!flag) {
            throw new BaseException("数据库删除设备失败");
        }
        // 再删iot
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.setDeviceId(iotId);
        DeleteDeviceResponse response = client.deleteDevice(request);
        if (response.getHttpStatusCode() != 204) {
            throw new BaseException("iot删除设备失败");
        }
    }

    @Override
    public List<ServiceCapability> queryProduct(String productKey) {
        // 参数校验
        if (StringUtils.isEmpty(productKey)) {
            throw new BaseException("产品key不能为空");
        }
        // 调用华为云IOT平台接口
        ShowProductRequest request = new ShowProductRequest();
        request.setProductId(productKey);
        ShowProductResponse response = client.showProduct(request);
        // 判断从iot查询的结果是否为空
        if (ObjectUtil.isEmpty(response)) {
            throw new BaseException("iot平台查询结果为空");
        }
        List<ServiceCapability> serviceCapabilityList = response.getServiceCapabilities();
        // 判断是否存在服务数据
        if (CollectionUtil.isEmpty(serviceCapabilityList)) {
            throw new BaseException("该产品未定义服务");
        }
        return serviceCapabilityList;
    }


}
